package org.openglcharts.listeners;

import java.util.ArrayList;
import java.util.Iterator;
import java.util.Vector;

import javax.media.opengl.GL2;
import javax.media.opengl.GLAutoDrawable;

import org.openglcharts.common.Value;

import com.jogamp.opengl.util.gl2.GLUT;

public class LineChart2DEventListener extends Chart2DEventListener {

	public static final int RESIZE_POLICY_NONE = 0;
	public static final int RESIZE_POLICY_CROP = 1;
	public static final int RESIZE_POLICY_SCALE = 2;

	private int resizePolicy = RESIZE_POLICY_CROP;

	private float selectionTolerance = 0.1f;

	private GLUT glut = new GLUT();

	private Vector<LineChart2DChannel> channels = new Vector<LineChart2DChannel>();

	public LineChart2DEventListener(int resizePolicy) {
		switch (resizePolicy) {
		case RESIZE_POLICY_NONE:
			this.resizePolicy = RESIZE_POLICY_NONE;
			break;
		case RESIZE_POLICY_CROP:
			this.resizePolicy = RESIZE_POLICY_CROP;
			break;
		case RESIZE_POLICY_SCALE:
			this.resizePolicy = RESIZE_POLICY_SCALE;
			break;
		default:
			this.resizePolicy = RESIZE_POLICY_CROP;
		}
	}

	@Override
	protected void initChart(GLAutoDrawable drawable) {
		// nothing to do here
	}

	@Override
	protected void disposeChart(GLAutoDrawable drawable) {
		// nothing to do here
	}

	@Override
	public void displayChart(GLAutoDrawable drawable) {
		GL2 gl = drawable.getGL().getGL2();
		ArrayList<Value> highlightedPoints = new ArrayList<Value>();

		// analyze cursor-position for highlights
		for (LineChart2DChannel channel : this.channels) {
			int bestIndex = channel.getFuzzy(this.cursorPosition.x);
			Value bestPoint = channel.get(bestIndex);
			if (Math.abs(this.cursorPosition.x - bestPoint.x) < this.selectionTolerance
					&& Math.abs(this.cursorPosition.y - bestPoint.y) < this.selectionTolerance) {
				channel.highlight(true);
				highlightedPoints.add(bestPoint);
			} else {
				channel.highlight(false);
			}
		}

		// draw channels
		for (LineChart2DChannel channel : this.channels) {
			gl.glColor3f(channel.getColor()[0], channel.getColor()[1],
					channel.getColor()[2]);
			gl.glBegin(GL2.GL_LINE_STRIP);
			Iterator<Value> it = channel.iteratorSubset(
					(int) Math.floor(this.anchor.x),
					(int) Math.ceil(this.anchor.x + this.width));
			Value value;
			synchronized (channel) {
				while (it.hasNext()) {
					value = it.next();
					gl.glVertex2f(value.x, value.y);
				}
			}
			gl.glEnd();
		}

		// draw highlighted values
		gl.glColor3f(1.0f, 1.0f, 1.0f);
		gl.glPointSize(5.0f);
		gl.glBegin(GL2.GL_POINTS);
		for (Value point : highlightedPoints) {
			gl.glVertex2f(point.x, point.y);
		}
		gl.glEnd();

		// draw labels for highlighted points
		for (Value point : highlightedPoints) {
			gl.glRasterPos2f(point.x + 0.02f, point.y);
			String string = "[" + Math.round(point.x * 100.0f) / 100.0f + ", " + Math.round(point.y* 100.0f) / 100.0f + "]";
			for (int i = 0; i < string.length(); i++) {
				glut.glutBitmapCharacter(GLUT.BITMAP_HELVETICA_10, string.charAt(i));
			}
		}
	}

	@Override
	protected void reshapeChart(GLAutoDrawable drawable, int x, int y,
			int width, int height) {
		// nothing to do here
	}

	public void addChannel(LineChart2DChannel channel) {
		this.channels.add(channel);
		channel.addValueListener(new LineChart2DChannelListener() {
			@Override
			public void valueAdded(Value value) {
				if (anchor.x + width < value.x) {
					switch (resizePolicy) {
					case RESIZE_POLICY_CROP:
						setDomain(new Value(value.x - width, anchor.y), width,
								height);
						break;
					case RESIZE_POLICY_SCALE:
						setDomain(anchor, value.x - anchor.x, height);
						break;
					default:
						break;
					}
				}
			}
		});
	}
}
